Luogu P2590 树的统计

基础树剖(从P3178的代码稍作修改便可A掉
这里和3178差别在于线段树的过程不同和一个deep数组,具体看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#include <bits/stdc++.h>
#pragma GCC optimize(3)//手开O3の日常
#define N 100010
using namespace std;
int n,m,fr,t,a,c,data[N],Size[N],num,p[2*N],fa[N],id,out[2*N],pos[2*N],h[2*N],belong[2*N],b[2*N],nt[2*N],son[N*2],deep[2*N];
char s[10];
struct node
{
int left,right,mx;
long long sum,val;
};struct node tree[4*N];//线段树
void pushdown(int p)
{
if(tree[p].val!=0)
{
tree[2*p].val=tree[p].val;
tree[2*p+1].val=tree[p].val;
tree[2*p].sum=(tree[2*p].right-tree[2*p].left+1)*tree[p].val;
tree[2*p+1].sum=(tree[2*p+1].right-tree[2*p+1].left+1)*tree[p].val;
tree[p].val=0;
}
}//lazy tag的下放
void dfs(int x,int dep)
{
deep[x]=dep;
Size[x]=1;
int e=p[x];
while(e>0)
{
int k=b[e];
if(fa[x]!=k)
{
fa[k]=x;//记录每个节点的父节点,方便向上跳
dfs(k,dep+1);
if(Size[k]>Size[son[x]])
son[x]=k;//不断更新该节点的重儿子
Size[x]+=Size[k];//更新该节点下方的节点数
}
e=nt[e];
}
}//搜索确定每个节点的深度与其下的结点个数
void DFS(int x,int bh)//bh为该节点所属重链的编号(编号为该重链起点编号
{
id++;
pos[x]=id;//搜索序记录
out[x]=id;//这个数组请忽视(做3178后忘了改QAQ
h[id]=x;//线段树上的位置
int e=p[x];
int k=0;
belong[x]=bh;//记录每个节点所属的重链编号
if(son[x])
{
DFS(son[x],bh);//优先搜索重儿子可得到重链
out[x]=max(out[x],out[son[x]]);
}
e=p[x];
while(e>0)
{
int kk=b[e];
if(fa[x]!=kk&&kk!=son[x])
{
DFS(kk,kk);
out[x]=max(out[x],out[kk]);
}
e=nt[e];
}//搜索轻链
}
void add(int u,int v)
{
++num;
b[num]=v;
nt[num]=p[u];
p[u]=num;
}//前向星存图
void build(int p,int l,int r)
{
tree[p].left=l;
tree[p].right=r;
if(l==r)
{
tree[p].sum=data[h[l]];
tree[p].mx=data[h[l]];
return;
}
int mid=(l+r)/2;
build(2*p,l,mid);
build(2*p+1,mid+1,r);
tree[p].sum=tree[2*p].sum+tree[2*p+1].sum;
tree[p].mx=max(tree[2*p].mx,tree[2*p+1].mx);
}//建树
void change(int p,int l,int r,long long d)
{
if(tree[p].left==l&&tree[p].right==r)
{
tree[p].sum=(r-l+1)*d;
tree[p].mx=d;
tree[p].val=d;
return;
}
pushdown(p);
int mid=(tree[p].left+tree[p].right)/2;
if(r<=mid)
change(2*p,l,r,d);
else if(l>mid)
change(2*p+1,l,r,d);
else
{
change(2*p,l,mid,d);
change(2*p+1,mid+1,r,d);
}
tree[p].sum=tree[2*p].sum+tree[2*p+1].sum;
tree[p].mx=max(tree[2*p].mx,tree[2*p+1].mx);
}//线段树修改操作(注意此处要同时维护区间最大值与区间和
long long query(int p,int l,int r)
{
if(tree[p].left==l&&tree[p].right==r)
return tree[p].sum;
pushdown(p);
int mid=(tree[p].left+tree[p].right)/2;
if(r<=mid)
return query(2*p,l,r);
else if(l>mid)
return query(2*p+1,l,r);
else
return query(2*p,l,mid)+query(2*p+1,mid+1,r);
}//查询区间和
int Query(int p,int l,int r)
{
if(tree[p].left==l&&tree[p].right==r)
return tree[p].mx;
pushdown(p);
int mid=(tree[p].left+tree[p].right)/2;
if(r<=mid)
return Query(2*p,l,r);
else if(l>mid)
return Query(2*p+1,l,r);
else
return max(Query(2*p,l,mid),Query(2*p+1,mid+1,r));
}//查询区间最大值
long long work(int x,int y)
{
long long sum=0;
while(belong[x]!=belong[y])
{
if(deep[belong[x]]<deep[belong[y]])
swap(x,y);
sum+=query(1,pos[belong[x]],pos[x]);
x=fa[belong[x]];
}
if(deep[x]>deep[y])
swap(x,y);
sum+=query(1,pos[x],pos[y]);
return sum;
}//QSUM的操作(解释见下)
int Work(int x,int y)
{
int ans=-214748;
while(belong[x]!=belong[y])
{
if(deep[belong[x]]<deep[belong[y]])
swap(x,y);//比较x和y所属重链起点的深浅,将较浅的向上跳
ans=max(ans,Query(1,pos[belong[x]],pos[x]));
x=fa[belong[x]];
}//如果x与y不在同一重链中,就重复执行操作
if(deep[x]>deep[y])
swap(x,y);
ans=max(ans,Query(1,pos[x],pos[y]));//x与y在同一重链中最后进行一次操作
return ans;
}//QMAX的操作
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d%d",&fr,&t);
add(fr,t);
add(t,fr);
}
for(int i=1;i<=n;i++)
scanf("%d",&data[i]);
dfs(1,1);
DFS(1,1);//先进行搜索确定了线段树上的编号再建树
build(1,1,n);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%s",s);
if(s[1]=='H')
{
scanf("%d%d",&a,&c);
change(1,pos[a],pos[a],c);
}
else if(s[1]=='M')
{
scanf("%d%d",&a,&c);
printf("%d\n",Work(a,c));
}
else
{
scanf("%d%d",&a,&c);
printf("%lld\n",work(a,c));
}
}
return 0;
}

然而跑的很慢
1208ms 7.92Mb

0%